package com.mop.springcloud.corebase.listener.def;

import com.mop.springcloud.corebase.MopApplicationInitializer;
import com.mop.springcloud.corebase.bean.NotifyData;
import com.mop.springcloud.corebase.bean.ServiceInfo;
import com.mop.springcloud.corebase.listener.NotificationHook;
import com.mop.springcloud.corebase.listener.ShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Class Name: MopBasicSpringApplicationRunListener
 * Create Date: 18-3-15 上午10:57
 * Creator: Chim·Zigui
 * Version: v1.0
 * Updater:
 * Date Time:
 * Description:
 * <p>
 * 猫扑基础SpringApplication的各个运行生命周期运行监听器，在
 * starting   -- >  environmentPrepared  -->  contextPrepared  -->  contextLoaded  -->  started  -->  running
 * 的各个加载周期中进行回调，同时，在started后，注册了应用/服务的停止信号监听器
 * <p>
 * 注册告警通知回调，用于发出服务停止告警
 */
public class MopBasicSpringApplicationRunListener implements SpringApplicationRunListener, Ordered {

    private static final Log logger = LogFactory.getLog(MopBasicSpringApplicationRunListener.class);

    private static ServiceInfo serviceInfo = null;
    private final SpringApplication application;
    private final String[] args;

    private static boolean initialized = false;

    public MopBasicSpringApplicationRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        serviceInfo = new ServiceInfo();
    }

    @Override
    public void starting() {
        serviceInfo.setBeginTime(System.currentTimeMillis());
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        serviceInfo.setJavaRuntime(environment.getProperty("java.runtime.version") + "\t"
                + environment.getProperty("java.class.version"));
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        String[] activeProfiles = context.getEnvironment().getActiveProfiles();
        if (activeProfiles.length > 1) {
            throw new UnsupportedOperationException("Active profiles not allow grate than 1");
        }

        this.initProperties(context, activeProfiles);

        serviceInfo.setStarterDuration(System.currentTimeMillis() - serviceInfo.getBeginTime());
        if (MopApplicationInitializer.isMopApplicationInitializerEnable()) {
            logger.info("MOP-LOGGER - On MOP spring boot service started.");

            MopApplicationInitializer initializer;
            try {
                initializer = context.getBean(MopApplicationInitializer.class);
            } catch (NoSuchBeanDefinitionException e) {
                return;
            }

            // Shutdown listener
            ShutdownListener listener = this.getShutdownListener(initializer);
            // Notification Hook
            NotificationHook notificationHook = this.getNotificationHook(initializer);

            logger.info("MOP-LOGGER - Using @EnableMopAutoConfiguration:\n" + initializer.printAppStartInfo(serviceInfo));

            registerShutdownHook(context, initializer, listener, notificationHook);
        }
    }

    /**
     * 从当前激活的配置文件中获取配置信息
     *
     * @param context        ApplicationContext
     * @param activeProfiles 激活的配置列表
     */
    private void initProperties(ConfigurableApplicationContext context, String[] activeProfiles) {
        String springApplicationName = "undefined";
        String serverPort = "random";
        if (activeProfiles.length != 0) {
            YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
            try {
                String profileName = "application-" + activeProfiles[0] + ".yml";
                List<PropertySource<?>> load = loader.load(profileName, context.getResource("classpath:" + profileName));
                for (PropertySource<?> propertySource : load) {
                    Object applicationName = propertySource.getProperty("spring.application.name");
                    Object serverPortVal = propertySource.getProperty("server.port");
                    if (applicationName != null) {
                        springApplicationName = String.valueOf(applicationName);
                    }
                    if (serverPortVal != null) {
                        serverPort = String.valueOf(serverPortVal);
                    }
                }
                serviceInfo.setApplicationName(springApplicationName);
                serviceInfo.setServerPort(serverPort);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 注册停机回调
     *
     * @param context          ApplicationContext
     * @param initializer      应用基础初始化器
     * @param listener         停机监听器
     * @param notificationHook 告警通知
     */
    private void registerShutdownHook(ConfigurableApplicationContext context,
                                      MopApplicationInitializer initializer,
                                      ShutdownListener listener,
                                      NotificationHook notificationHook) {
        if (!initialized) {
            synchronized (MopBasicSpringApplicationRunListener.class) {
                if (!initialized) {
                    initialized = true;

                    Runtime.getRuntime().addShutdownHook(new Thread() {
                        @Override
                        public void run() {
                            if (listener != null)
                                listener.beforeShutdown(context);

                            this.notifyPostProcessing();

                            if (listener != null)
                                listener.onShutdown(context);
                            // 处理完成所有未处理的数据

                            if (listener != null)
                                listener.stopped(context);

                            serviceInfo.setEndTime(System.currentTimeMillis());
                            this.notifyStopped();

                            logger.info("MOP-LOGGER - Using @EnableMopAutoConfiguration:\n" + initializer.printAppEndInfo(serviceInfo));
                        }

                        /**
                         * 服务/应用已经停止告警
                         */
                        private void notifyStopped() {
                            // 发出正式停机告警，获取系统环境，同时打包到告警信息中
                            if (notificationHook != null) {
                                Map<String, Object> data = new HashMap<String, Object>();
                                data.put("begin_time", new Date(serviceInfo.getBeginTime()));
                                data.put("end_time", new Date(serviceInfo.getEndTime()));
                                notificationHook.notify(new NotifyData("服务已经下线", data));
                            }
                        }

                        /**
                         * 服务/应用收到停止信号，准备执行后续的结束任务
                         */
                        private void notifyPostProcessing() {
                            if (notificationHook != null && listener != null) {
                                Map<String, Object> data = new HashMap<String, Object>();
                                data.put("begin_time", new Date(serviceInfo.getBeginTime()));
                                notificationHook.notify(new NotifyData("服务即将下线", "停机处理中", data));
                            }
                        }
                    });
                }
            }

        }
    }

    /**
     * 获得停机监听器
     *
     * @param initializer 猫扑基础初始化器
     * @return 停机监听器
     */
    private ShutdownListener getShutdownListener(MopApplicationInitializer initializer) {
        ShutdownListener listener = initializer.getShutdownListener();
        if (listener != null) {
            serviceInfo.setShutdownListenerRegistered(true);
            logger.info("MOP-LOGGER - Found mop application initializer, register shutdown listener.");
        } else {
            logger.warn("MOP-LOGGER - Could not found any shutdown listener in current application.");
        }
        return listener;
    }

    /**
     * 告警初始化器
     *
     * @param initializer 猫扑基础初始化器
     * @return 告警初始化器
     */
    private NotificationHook getNotificationHook(MopApplicationInitializer initializer) {
        NotificationHook notificationHook = initializer.getNotificationHook();
        if (notificationHook != null) {
            serviceInfo.setRegisterNotificationHook(true);
            logger.info("MOP-LOGGER - Found mop application initializer, register notification hook listener.");
        } else {
            logger.warn("MOP-LOGGER - Could not found any notification hook listener in current application.");
        }
        return notificationHook;
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        if (MopApplicationInitializer.isMopApplicationInitializerEnable()) {
            logger.info("MOP-LOGGER - On MOP spring boot service running.");
        }
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        if (MopApplicationInitializer.isMopApplicationInitializerEnable()) {
            logger.info("MOP-LOGGER - On MOP spring boot service failed.");
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
